// Copyright (c) 2020 Presto Labs Pte. Ltd.
// Author: donggu

#pragma once

#include <cstdlib>
#include <string>
#include <memory>
#include <atomic>

#include "coin2/base/log.h"
#include "coin2/base/time.h"
#include "coin2/base/scheduler.h"
#include "coin2/exchange/base/feed/ws/feed_subscription_spec.h"
#include "coin2/exchange/base/executor/live_executor.h"
#include "coin2/exchange/base/feed/ws/fix_connection.h"

#include "quickfix/Application.h"
#include "quickfix/MessageCracker.h"
#include "quickfix/SessionSettings.h"
#include "quickfix/fix42/MarketDataRequest.h"
#include "quickfix/fix42/Logon.h"
#include "quickfix/fix42/TestRequest.h"
#include "quickfix/fix42/Heartbeat.h"
#include "quickfix/fix42/ResendRequest.h"
#include "quickfix/fix42/SequenceReset.h"
#include "quickfix/fix42/Reject.h"

namespace coin2::exchange::lmax_digital::feed_v1 {

class FixConnection : public std::enable_shared_from_this<FixConnection>,
                      public base::feed::FixConnection,
                      public FIX::MessageCracker {
 public:
  explicit FixConnection(
    base::executor::LiveAsyncExecutor* exec,
    const WsConnectionConfig& config);
  ~FixConnection();

  std::shared_ptr<FIX::SessionSettings> GetSessionSettings();

  //
  // implement interface FIX::Application
  //
  void onCreate(const FIX::SessionID&) override;
  void onLogon(const FIX::SessionID&) override;
  void onLogout(const FIX::SessionID&) override;
  void toAdmin(FIX::Message&, const FIX::SessionID&) override;
  void toApp(FIX::Message&, const FIX::SessionID&) EXCEPT(FIX::DoNotSend) override;
  void fromAdmin(const FIX::Message&, const FIX::SessionID&)
      EXCEPT(FIX::FieldNotFound, FIX::IncorrectDataFormat, FIX::IncorrectTagValue, FIX::RejectLogon)
          override;
  void fromApp(const FIX::Message&, const FIX::SessionID&) EXCEPT(
      FIX::FieldNotFound,
      FIX::IncorrectDataFormat,
      FIX::IncorrectTagValue,
      FIX::UnsupportedMessageType) override;

 private:
  // for crack fuction
  void onMessage(FIX42::Logon& message, const FIX::SessionID& sessionID) override;
  void onMessage(FIX42::TestRequest& message, const FIX::SessionID& sessionID) override;
  void onMessage(FIX42::ResendRequest& message, const FIX::SessionID& sessionID) override;
  void onMessage(FIX42::SequenceReset& message, const FIX::SessionID& sessionID) override;
  void onMessage(FIX42::Reject& message, const FIX::SessionID& sessionID) override;

  void BuildLogonRequest(FIX::Message* message, const FIX::SessionID& sessionID);
  void SendSubscribeRequest(FIX42::MarketDataRequest* message, const FIX::SessionID& sessionID);
  void BuildSubscribeRequest(
      FIX42::MarketDataRequest* message,
      const FIX::SessionID& sessionID,
      const std::string& symbol);
  void BuildHeaderTrailer(FIX::Message* message, const FIX::SessionID& session_id,
      const std::string& msg_type);
  void ToPrintMessage(std::string* message);
  void Resubscribe();

  base::executor::LiveAsyncExecutor* const exec_;
  std::shared_ptr<Scheduler> scheduler_;
  const WsConnectionConfig config_;
  std::shared_ptr<FIX::SessionSettings> session_settings_;
  int msg_seq_num_ = 1;
  FIX::SessionID default_session_;
  std::atomic<bool> is_logon_ = false;
};

}  // namespace coin2::exchange::lmax_digital::feed_v1
